/**
 * CyclicBarrier的使用
 *
 * @author fireway
 * @since 2019年 03月 04日 星期一 07:02:21 CST
 */
package concurrency;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class Horse implements Runnable {
    private static CyclicBarrier sCyclicBarrier;
    
    private static int sCounter = 0;
    
    private final int mId = sCounter++;
    
    private int mStrideTotalNum = 0;
    
    private static Random sRand = new Random(47);
    
    public Horse(CyclicBarrier b) { 
        sCyclicBarrier = b; 
    }
    
    public String getTrack() {
        StringBuilder s = new StringBuilder();
        for(int i = 0; i < mStrideTotalNum; i++) {
            s.append("*");
        } 
        s.append(mId);
        return s.toString();
    }
    
    public /*synchronized*/ int getStrideTotalNum() { 
        return mStrideTotalNum; 
    }
    
    @Override
    public String toString() {
        return "Horse [id=" + mId + "]";
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                // synchronized (this) {
                    mStrideTotalNum += sRand.nextInt(3); // Produces 0, 1 or 2
                // }
                sCyclicBarrier.await();
            }
        } catch (InterruptedException e) {
            // A legitimate way to exit
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            // This one we want to know about
            e.printStackTrace();
        }
    }
}

public class HorseRace {
    private CyclicBarrier mCyclicBarrier;
    
    private ExecutorService mExecutorService = Executors.newCachedThreadPool();
    
    private List<Horse> mHorses = new ArrayList<Horse>();
    
    private static final int FINISH_LINE = 75;
    
    public HorseRace(int horseNum, final int pause) {
        mCyclicBarrier = new CyclicBarrier(horseNum, new Runnable() {
            @Override
            public void run() {
                StringBuilder s = new StringBuilder();
                for(int i = 0; i < FINISH_LINE; i++) {
                    s.append("="); // The fence on the racetrack
                }
                System.out.println(s);
                for(Horse horse : mHorses) {
                    System.out.println(horse.getTrack());
                }
                for(Horse horse : mHorses) {
                    if(horse.getStrideTotalNum() >= FINISH_LINE) {
                        System.out.println(horse + " won!");
                        mExecutorService.shutdownNow();
                        return;
                    }
                }
                try {
                    TimeUnit.MILLISECONDS.sleep(pause);
                } catch (InterruptedException e) {
                    System.out.println("barrier-action sleep interrupted");
                    e.printStackTrace();
                }                
            }});
        
        for(int i = 0; i < horseNum; i++) {
            Horse horse = new Horse(mCyclicBarrier);
            mHorses.add(horse);
            mExecutorService.execute(horse);
        }
    }

    public static void main(String[] args) {
        int horseNum = 7;
        int pause = 200;
        if (args.length > 0) {
            int n = new Integer(args[0]);
            horseNum = n > 0 ? n : horseNum;
        }
        if (args.length > 1) { // Optional argument
            int p = new Integer(args[1]);
            pause = p > -1 ? p : pause;
        }
        
        new HorseRace(horseNum, pause);
    }
}
